﻿/*******************************************************************************
** 全局信号传递机制实现类。
** 实现此类需要考虑并解决的问题：
** 1.实现信号注册机制，不同对象实例可以注册成同一个全局信号，前提是全局信号签名相同。
** 2.可以手动取消注册全局信号，也可以在信号所在对象销毁时自动取消注册。
** 3.实现槽注册机制，在当前全局中暂时没有某信号时，也可以监听，等到有对应的信号注册了，自动连接上。
** 4.可以手动取消槽注册，也可以在槽所在对象销毁时自动取消。
** 5.处理好动态新增信号情况下，槽的连接问题。
*******************************************************************************/

#include "ZGlobalSignal.h"
#include <QDebug>

ZFLIB_BEGIN_NAMESPACE

ZGlobalSignal *ZGlobalSignal::instance()
{
    static ZGlobalSignal instance;
    return &instance;
}

ZGlobalSignal::~ZGlobalSignal()
{
    qDeleteAll(m_signal_view); m_signal_view.clear();
    qDeleteAll(m_sender_view); m_sender_view.clear();
    qDeleteAll(m_recver_view); m_recver_view.clear();
}

void ZGlobalSignal::doEmit(QObject *sender, const char *signal)
{
    QMutexLocker locker(&m_mutex);

    // 参数检查
    if (!sender)
    {
        qWarning() << "sender should not be null!" << Q_FUNC_INFO;
        return;
    }

    // 保存信号、发送者到信号视图，以便通过信号找到所有发送者和接收者 =====
    SignalInfo *sig_info = m_signal_view.value(signal);
    if (!sig_info) // 首次注册
    {
        // 分配资源
        sig_info = new SignalInfo;
        sig_info->signal_name = signal;
        m_signal_view.insert(signal, sig_info);
    }

    // 添加信号发送者
    if (!sig_info->sender_list.contains(sender))
    {
        sig_info->sender_list.append(sender);
    }
    // =====

    // 保存发送者和信号到发送者视图，以便可以通过发送者找到其所有全局信号 =====
    SenderInfo *sender_info = m_sender_view.value(sender);
    if (!sender_info)
    {
        // 新建sender信息
        sender_info = new SenderInfo;
        m_sender_view.insert(sender, sender_info);

        // 监听发送者销毁的信号，做清理工作
        connect(sender, SIGNAL(destroyed(QObject *)), SLOT(slotSenderDestoryed(QObject *)));
    }

    if (!sender_info->signal_list.contains(signal))
    {
        sender_info->signal_list.append(signal);
    }
    // =====

    // 连接已注册的响应槽 =====
    int slot_count = sig_info->slot_list.size();
    for (int i = 0; i < slot_count; i++)
    {
        SlotInfo *si = sig_info->slot_list.at(i);
        if (!si->recver)
        {
            sig_info->slot_list.removeAt(i);
            i--;
            slot_count--;
            continue;
        }

        QByteArray ba = si->slot_name.toLatin1();
        connect(sender, signal, si->recver, ba.constData());
    }
    // =====
}

void ZGlobalSignal::undoEmit(QObject *sender, const char *signal)
{
    QMutexLocker locker(&m_mutex);

    SenderInfo *sender_info = m_sender_view.value(sender);
    if (!sender_info)
    {
        qWarning() << "something error!" << Q_FUNC_INFO;
        return;
    }

    // 清理信号视图
    SignalInfo *sig_info = m_signal_view.value(signal);
    Q_ASSERT(sig_info);

    // 断开连接
    foreach (SlotInfo *si, sig_info->slot_list)
    {
        QByteArray ba = si->slot_name.toLatin1();
        disconnect(sender, signal, si->recver, ba.constData());
    }

    // 删除此引用
    sig_info->sender_list.removeOne(sender);

    // 如果清空了，则释放内存，删除条目
    if (sig_info->sender_list.isEmpty())
    {
        delete sig_info;
        sig_info = NULL;
        m_signal_view.remove(signal);
    }

    // 清理发送者视图
    sender_info->signal_list.removeOne(signal);

    // 如果清空了，则释放内存，删除条目
    if (sender_info->signal_list.isEmpty())
    {
        delete sender_info;
        sender_info = NULL;
        m_sender_view.remove(sender);
    }
}

void ZGlobalSignal::doSlot(const char *signal, QObject *recver, const char *slot)
{
    QMutexLocker locker(&m_mutex);

    // 自动注册此槽
    SignalInfo *sig_info = m_signal_view.value(signal);
    if (!sig_info)
    {
        sig_info = new SignalInfo;
        sig_info->signal_name = signal;
        m_signal_view.insert(signal, sig_info);
    }

    SlotInfo *si = new SlotInfo;
    si->recver = recver;
    si->slot_name = slot;
    si->signal_name = signal;
    sig_info->slot_list.append(si);

    // 建立发送者接收者信号槽连接
    foreach (QObject *sender, sig_info->sender_list)
    {
        connect(sender, signal, recver, slot);
    }

    // 保存接收者信息
    RecverInfo *ri = m_recver_view.value(recver);
    if (!ri)
    {
        ri = new RecverInfo;
        if (!ri->signal_list.contains(signal))
        {
            ri->signal_list.append(signal);
        }

        m_recver_view.insert(recver, ri);
    }

    // 监视接收者的生命周期
    connect(recver, SIGNAL(destroyed(QObject *)), this, SLOT(slotRecverDestoryed(QObject *)));
}

void ZGlobalSignal::undoSlot(const char *signal, QObject *recver, const char *slot)
{
    QMutexLocker locker(&m_mutex);

    // 找到信号信息
    SignalInfo *sig_info = m_signal_view.value(signal);
    if (!sig_info)
    {
        qWarning() << "No such global signal:" << signal << Q_FUNC_INFO;
        return;
    }

    // 依次释放连接
    foreach (QObject *sender, sig_info->sender_list)
    {
        disconnect(sender, signal, recver, slot);
    }

    // 删除槽注册记录
    int count = sig_info->slot_list.size();
    for (int i = 0; i < count; i++)
    {
        SlotInfo *si = sig_info->slot_list.at(i);
        if (si->recver == recver && si->slot_name == slot)
        {
            // 删除此槽
            sig_info->slot_list.removeAt(i);
            delete si; si = NULL;
            break;
        }
    }

    // 删除接收者信息
    RecverInfo *ri = m_recver_view.value(recver);
    if (ri)
    {
        delete ri; ri = NULL;
        m_recver_view.remove(recver);
    }
}

void ZGlobalSignal::slotSenderDestoryed(QObject *sender)
{
    QMutexLocker locker(&m_mutex);

    SenderInfo *sender_info = m_sender_view.value(sender);
    if (!sender_info)
    {
        qWarning() << "something error!" << Q_FUNC_INFO;
        return;
    }

    // 清理信号视图
    foreach (const QString &signal, sender_info->signal_list)
    {
        SignalInfo *sig_info = m_signal_view.value(signal);
        Q_ASSERT(sig_info);

        // 删除此引用
        sig_info->sender_list.removeOne(sender);

        // 如果发送者和接收者都为空，则删除此信号相关数据
        if (sig_info->sender_list.isEmpty() &&
            sig_info->slot_list.isEmpty())
        {
            delete sig_info;
            sig_info = NULL;
            m_signal_view.remove(signal);
        }
    }

    // 清理发送者视图
    m_sender_view.remove(sender);
    delete sender_info; sender_info = NULL;
}

void ZGlobalSignal::slotRecverDestoryed(QObject *recver)
{
    QMutexLocker locker(&m_mutex);

    RecverInfo *ri = m_recver_view.value(recver);
    if (!ri) return;

    foreach (const QString &signal_name, ri->signal_list)
    {
        SignalInfo *si = m_signal_view.value(signal_name);
        if (!si) continue;

        int slot_count = si->slot_list.size();
        for (int i = 0; i < slot_count; i++)
        {
            SlotInfo *slot_info = si->slot_list.at(i);
            if (slot_info->recver == recver)
            {
                si->slot_list.removeAt(i);
                i--;
                slot_count--;
                continue;
            }
        }

        // 发送者和接收者均为空，则清除相关数据
        if (si->slot_list.isEmpty() && si->sender_list.isEmpty())
        {
            delete si;
            si = NULL;
            m_signal_view.remove(signal_name);
        }
    }

    // 删除接收者信息
    m_recver_view.remove(recver);
    delete ri; ri = NULL;
}

ZGlobalSignal::ZGlobalSignal(QObject *parent) : QObject(parent)
{
}

ZFLIB_END_NAMESPACE
